home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr05 / xnot12a.zip / XWIN.C < prev   
C/C++ Source or Header  |  1993-06-13  |  34KB  |  1,292 lines

  1. #ifdef X11 /** WHOLE FILE **/
  2.  
  3. /*
  4.     This is a quickie port of a MsWindows version of MicroEMACS to
  5.     X11 (Un*x flavor). The original code ran under DOS to a bios interface
  6.     with direct hardware access to the keyboard and system clock. This
  7.     version and the following X files have been (mostly) hooked in underneath
  8.     the original code, which thankfully had most of the terminal io (screen
  9.     access) and keyboard input at a level distinct from the core editor. Not
  10.     every function is relegated to the absolute best .c file location (for
  11.     example, some system calls are implemented inside xwin.c vs unix.c for making
  12.     shell commands). In addition, much bashing was done to make
  13.     this Emacs look more like GNU-Emacs (make/next-error functions), automatic
  14.     timed backups, timestamp checking, and a large assortment of functions
  15.     implemented to extend the editor and move it away from MicroEMACS to
  16.     GNU-emacs feel. Hopefully a reasonable result given the amount of time 
  17.     spent. 
  18.  
  19.     The goal was not to make emacs really window-aware. Instead, the window 
  20.     specific code was to act like a terminal. That is why the startup code (X11)
  21.     executes first; to create a window, etc such that when EmaxMain is called,
  22.     the 'terminal' (ie window) exists. The input handling is done by creating
  23.     an internal queue at the 'terminal' level and emacs just reads chars from
  24.     the 'keyboard' buffer. Special window-specific things happen without core
  25.     code being aware (ie focus events, etc). Events which require a core code
  26.     response, like window resize, generate emacs recognizable keycodes (or
  27.     entire command strings) which the editor can get. Resize in specific 
  28.     generates redraw-display and the core code already did a terminal size 
  29.     query for that command. 
  30.  
  31.     Mouse events are funny; window specific code handles them as far as
  32.     determining double click, etc. But window level code only goes as far
  33.     as converting mouse events to emacs commands for left, right, up/down,
  34.     etc. In other words, emacs is now mouse-aware, but in an abstract way.
  35.     A wayward user can use the keyboard to send mouse actions, though not
  36.     easily. Only window level code can parse a mouse click/motion to rows
  37.     and columns that emacs needs. This is based on font, window size, etc. 
  38.  
  39.     Note that there are typedefs and defines which look like MS Windows
  40.     things; I ported the Dos version to Windows 3.1 first and moved as much
  41.     as I could straight to X11-land. Blame that for the code organization! 
  42.  
  43.     X source files:
  44.  
  45.     jam.h                  version defines, includes, etc 
  46.     xwin.c (this file)     main, event loop, window creation, etc
  47.     xio.c  (aka ttyio.c)   character output, scrolling, etc
  48.     xkey.c                 map keypress event to some EMACS virtual key
  49.                              or extended commands
  50.     unix.c                 not X11 at all, but some un*x flavored things
  51. */  
  52.  
  53. #define XWIN_C        /* for extern of globals */
  54.   
  55. #include "jam.h"
  56. #include "def.h"
  57. #include "keyname.h"
  58. #include "stdio.h"
  59. #include "ttydef.h"
  60. #include "chrdef.h"
  61. #include "time.h"   
  62.  
  63. #include "keysym.h"
  64. #ifdef HP
  65. # include "HPkeysym.h"
  66. #endif
  67.  
  68. #ifdef INTERACTIVE
  69. # include "bsdtypes.h"
  70. # define pid_t int            /* major ughlyness due to include bug */
  71. #endif
  72.  
  73. #include <sys/time.h>  
  74. #include <signal.h>
  75. #include <sys/types.h>
  76. #include <unistd.h>
  77.  
  78. #if defined(SOL) || defined (HP)
  79. # include <sys/resource.h>
  80.  struct rlimit rlim;
  81. #endif
  82.  
  83. /* Globals for all of emacs
  84. */
  85. Display *gDpy = 0;
  86. Window gWindow = 0;
  87. XFontStruct *gFont = 0;
  88. XCharStruct *gXchar = 0;
  89. GC gText = NULL;
  90. GC gTouchedText = NULL;
  91. GC gMode = NULL;
  92. GC gXor = NULL;
  93. int g_nLineHeight, g_nCharWidth, g_nLineAscent;
  94. BOOL g_hasFocus = FALSE;
  95. BOOL edInited = FALSE;
  96. int g_caret = 0;
  97. int g_caret_x, g_caret_y;
  98.  
  99. /* Local variables
  100. */
  101. static XEvent s_event;
  102. static Cursor s_hand = 0, s_pointer = 0, s_wait = 0, s_size = 0, s_arrow;
  103. static char s_timestr[35] = {0};
  104. static BOOL s_btndown = FALSE;
  105. static BOOL s_fatal = FALSE;
  106. static int s_sTicks = 0;          /* inc-save interval */
  107. static char *blank = " ";
  108. static char *fontname = def_fontname;
  109. static char *display = 0;
  110. static int s_scrollTicks = 0;     /* autoscroll */
  111. static pid_t child = (pid_t) -1;
  112. static BOOL logready = FALSE;
  113. static unsigned long s_textcolor, s_touchedtextcolor, s_windowcolor;
  114. static char *tcolor = NULL, *ccolor = NULL, *wcolor = NULL;
  115. static Colormap s_cmap;
  116. static int s_screen;
  117.  
  118. /* Local functions
  119. */
  120. static void rn_(SetTheTitle, (void));
  121. static void rn_(GetTheTime, (void));
  122. static void rn_(InitX, (int ac, char **av, int *nac, char ** nav));
  123. static int rn_(DoGCs, (void));
  124. static void rn_(DoTimer, (void));
  125. static void rn_(use, (void));
  126. static void *rn_(childdone, (void));
  127. static void rn_(getjoblog, (void));
  128. static int rn_(newcolor, (int which));
  129. static void rn_(ReverseCaret, (GC gc));
  130.  
  131. /* Which mouse button - Note LEFT is button 1 and RIGHT is button 2!!!!
  132. * because this code migrated from Windows where 2 button mouses are the 
  133. * norm (yes, it is icky).
  134. */
  135. #define LEFT    0x1
  136. #define RIGHT    0x2
  137.  
  138. BOOL WindowFatalState()
  139. {
  140.   return(s_fatal);
  141. }
  142.  
  143. static int errorhandler(dpy, event)
  144. Display *dpy;
  145. XErrorEvent *event;
  146. {
  147. #define ErrorLen 128
  148.  char msg[ErrorLen];
  149.  
  150.  XGetErrorText(dpy, event->error_code, msg, ErrorLen);
  151.  printf("Xlib error: request %d minor %d \n\t%s\n", 
  152.         event->request_code, event->minor_code, msg);
  153.  ttbeep();
  154. }
  155. static int ioerrorhandler(dpy)
  156. Display *dpy;
  157. {
  158.   if (anycb(ABORT))
  159.     printf("Fatal XIO error, flushing changes!\n");
  160.   ealtmsg = TRUE;
  161.   s_fatal = TRUE;
  162.   IncrementalSave();
  163.   exit (-1);        /* we're dead anyway */
  164. }
  165.  
  166. /* X support functions
  167. */
  168. int NewFont(f, n)
  169. int f, n;
  170. {
  171.   XFontStruct *font;
  172.   char newname[NFILEN*2];
  173.   int s;
  174.  
  175.   s = eread("New font name:", newname, NFILEN*2, EFNEW);
  176.   if (s != TRUE)
  177.     return (s);
  178.  
  179.   if (!(font = XLoadQueryFont(gDpy,newname)))
  180.     {
  181.       ewprintf("Can't load requested font %s.", newname);
  182.       s = ABORT;
  183.     }
  184.   else
  185.     {
  186.       XFreeFont(gDpy, gFont);        /* release old font */
  187.       gFont = font;            /* replace the font */
  188. #if 1   /* easier this way */
  189.       DoGCs();                /* reset the GC values */
  190. #else
  191.       gXchar = &gFont->max_bounds;    /* recompute all info */
  192.       gXchar = &gFont->max_bounds;
  193.       g_nLineHeight = gFont->ascent + gFont->descent + FudgeHeight; 
  194.       if (g_nLineHeight % 2)
  195.         g_nLineHeight++;            /* force even # pixels */
  196.       g_nCharWidth = gXchar->width;
  197.       XSetFont(gDpy, gMode, gFont->fid);    /* update gc's */
  198.       XSetFont(gDpy, gText, gFont->fid);
  199.       XSetFont(gDpy, gTouchedText, gFont->fid);
  200.       XSetFont(gDpy, gXor, gFont->fid);        /* overkill,cursor gc only */
  201. #endif
  202.       myrefresh(0, 1);
  203.       WindowSync();
  204.     }
  205. }
  206. int NewTextColor(f, n)
  207. int f, n;
  208. {
  209.   return(newcolor(CTEXT));
  210. }
  211. int NewTouchedTextColor(f, n)
  212. int f, n;
  213. {
  214.   return(newcolor(CHIGH));
  215. }
  216. int NewWindowColor(f, n)
  217. int f, n;
  218. {
  219.   int s = newcolor(CMODE);
  220.   
  221.   if (s)
  222.    XSetWindowBackground(gDpy, gWindow, s_windowcolor);
  223.  
  224.   return(s);
  225. }
  226. /* work function for color changing
  227. */
  228. static int newcolor(type)
  229. int type;
  230. {
  231.   char color[NFILEN*2];
  232.   int s;
  233.   XColor colorcell, rgb;
  234.   char *msg;
  235.  
  236.   if (type == CTEXT)
  237.     msg = "New text color: ";  
  238.   else if (type == CHIGH)
  239.     msg = "New touched text color: ";
  240.   else if (type == CMODE)
  241.     msg = "New window color: ";
  242.  
  243.   s = eread(msg, color, NFILEN*2, EFNEW);
  244.   if (s != TRUE)
  245.     return (s);
  246.  
  247.   if (!XAllocNamedColor(gDpy, s_cmap, color, &colorcell, &rgb))
  248.     ewprintf("Unable to allocate color");
  249.   else
  250.     {
  251.       if (type == CTEXT)
  252.         s_textcolor = colorcell.pixel;
  253.       else if (type == CHIGH)
  254.         s_touchedtextcolor = colorcell.pixel;
  255.       else if (type == CMODE)
  256.         s_windowcolor = colorcell.pixel;
  257.       else
  258.         {
  259.           ewprintf("Internal error changing colors");
  260.           return(FALSE);
  261.         }
  262.       DoGCs();
  263.       myrefresh(0, 1);
  264.       WindowSync();
  265.     }
  266.   return(s);
  267. }
  268.  
  269. /* Main entry point, ta da!
  270. */
  271. static char *whoami;
  272.  
  273. main(argc, argv)      
  274. int   argc;
  275. char *argv[];
  276. {
  277. #define MAXARGS 500
  278.   int myargc = 0;
  279.   char *myargv[MAXARGS];
  280.   XColor colorcell, rgb;
  281.  
  282.   /* Open the device based on user input; create
  283.    * drawing context and open a default font
  284.    */
  285.   whoami = argv[0];    /* progname */
  286.  
  287.   if (argc > MAXARGS)
  288.     {
  289.       printf("Number of input args too large! Truncating to %d\n", MAXARGS);
  290.       argc = MAXARGS;
  291.     }
  292.   InitX(argc, argv, &myargc, myargv);
  293.  
  294.   /* Init colors, drawing GC's
  295.   */
  296.   if (tcolor && XAllocNamedColor(gDpy, s_cmap, tcolor, &colorcell, &rgb))
  297.     s_textcolor = colorcell.pixel;
  298.   else
  299.     s_textcolor = BlackPixel(gDpy, s_screen);
  300.   if (ccolor && XAllocNamedColor(gDpy, s_cmap, ccolor, &colorcell, &rgb))
  301.     s_touchedtextcolor = colorcell.pixel;
  302.   else
  303.     s_touchedtextcolor = BlackPixel(gDpy, s_screen);
  304.   if (wcolor && XAllocNamedColor(gDpy, s_cmap, wcolor, &colorcell, &rgb))
  305.     s_windowcolor = colorcell.pixel;
  306.   else
  307.     s_windowcolor = WhitePixel(gDpy, s_screen);
  308.   DoGCs();
  309.  
  310.   /* Cursors (mouse pointers...)
  311.   */
  312.   s_hand = XCreateFontCursor(gDpy, XC_xterm);
  313.   s_pointer = XCreateFontCursor(gDpy, XC_top_left_arrow);
  314.   s_wait = XCreateFontCursor(gDpy, XC_watch); 
  315.   s_size = XCreateFontCursor(gDpy, XC_double_arrow);
  316.   s_arrow = XCreateFontCursor(gDpy, XC_question_arrow);
  317.   
  318.   /* Initially size window based on font
  319.    */
  320.   InitSizeWindow(ncol = START_COLS, nrow = START_LINES);
  321.  
  322.   /* Init internal input buffer(s)
  323.    */
  324.   InitInput();
  325.  
  326.   /* Set signals to be ignored
  327.   */
  328.   signal(SIGQUIT, SIG_IGN);
  329.   signal(SIGINT, SIG_IGN);
  330.   signal(SIGHUP, SIG_IGN);
  331.  
  332.   /* Capture errors.
  333.   */
  334.   XSetIOErrorHandler(ioerrorhandler);    /* fatal */
  335.   XSetErrorHandler(errorhandler);    /* non fatal */
  336.  
  337.   /* Default title, make window visible, set cursor...
  338.   */
  339.   WindowNormalCursor();
  340.   XStoreName(gDpy, gWindow, g_APPNAME);
  341.   XMapWindow(gDpy, gWindow);
  342.   XSync(gDpy, TRUE);        /* sync, throw away all previous events */
  343.  
  344.   /*
  345.    * Call 'main' body of editor. Note it trys to open the
  346.    * terminal, etc but those functions are now noops..
  347.    */
  348.   edInited = TRUE; /* any message after this is ok to process */
  349.   EmaxMain(myargc, myargv);
  350.   return(0);
  351. }
  352.  
  353. /* Create/modify gcs for CTEXT, CHIGH and CMODE colors and cursor;
  354. * This will sometimes unnecessarily reset some values but it's easier 
  355. * to capture all the code in one place.
  356. */
  357. static int DoGCs()
  358. {
  359.   XGCValues _gcv, *gcv;
  360.   XVisualInfo vinfo;
  361.   
  362.   /* This will only abort the editor if called the
  363.   * first time; after that, there should be a font
  364.   */
  365.   if (!gFont)
  366.     if (!(gFont = XLoadQueryFont(gDpy, fontname)))
  367.       {
  368.         printf("Can't load font %s, trying 'fixed'\n", fontname);
  369.         if (!(gFont = XLoadQueryFont(gDpy, "fixed")))
  370.           {
  371.             printf("Can't load fixed font\n");
  372.             use();
  373.             exit(-1);
  374.           }
  375.      }
  376.   gcv = &_gcv;
  377.   gXchar = &gFont->max_bounds;
  378.   g_nCharWidth = gXchar->width;
  379.   g_nLineHeight = gFont->ascent + gFont->descent + FudgeHeight; 
  380.  
  381.   /* GC for text 
  382.   */
  383.   gcv->foreground = s_textcolor;
  384.   gcv->background = s_windowcolor;
  385.   gcv->function = GXcopy;
  386.   gcv->font = gFont->fid;
  387.   if (!gText)
  388.     gText = XCreateGC(gDpy, gWindow, 
  389.                 GCForeground|GCBackground|GCFunction|GCFont, gcv);
  390.   else
  391.     XChangeGC(gDpy, gText, GCForeground|GCBackground|GCFunction|GCFont, gcv);
  392.  
  393.   /* GC for window background
  394.   */
  395.   gcv->foreground = s_windowcolor;
  396.   gcv->background = s_textcolor;
  397.   gcv->function = GXcopy;
  398.   gcv->font = gFont->fid;
  399.   if (!gMode)
  400.     gMode = XCreateGC(gDpy, gWindow, 
  401.                       GCForeground|GCBackground|GCFunction|GCFont, gcv);
  402.   else
  403.     XChangeGC(gDpy, gMode, GCForeground|GCBackground|GCFunction|GCFont, gcv);
  404.  
  405.   /* GC for touched text
  406.   */
  407.   gcv->foreground = s_touchedtextcolor;
  408.   gcv->background = s_windowcolor;
  409.   gcv->function = GXcopy;
  410.   gcv->font = gFont->fid;
  411.   if (!gTouchedText)
  412.     gTouchedText = XCreateGC(gDpy, gWindow, 
  413.                  GCForeground|GCBackground|GCFunction|GCFont, gcv);
  414.   else
  415.     XChangeGC(gDpy, gTouchedText, GCForeground|GCBackground|GCFunction|GCFont, 
  416.               gcv);
  417.  
  418.   /* GC for XOR
  419.   */
  420.   gcv->foreground = s_textcolor;
  421.   gcv->background = s_windowcolor;
  422.  
  423.   /* This is not a good test; but invert works pretty
  424.   * well on the color and monochrome servers I tried, and xor works 
  425.   * much better on the grayscale SPARCstation...
  426.   */
  427. # ifdef SOL
  428.   if (!XMatchVisualInfo(gDpy, s_screen, 8, StaticGray, &vinfo) &&
  429.       !XMatchVisualInfo(gDpy, s_screen, 8, GrayScale, &vinfo))
  430. # else
  431.   if (1)
  432. # endif
  433.    gcv->function = GXinvert;
  434.   else
  435.     gcv->function = GXxor;
  436.   gcv->plane_mask = XAllPlanes();
  437.   gcv->font = gFont->fid;
  438.   if (!gXor)
  439.     gXor = XCreateGC(gDpy, gWindow, 
  440.                  GCForeground|GCBackground|GCFunction|GCPlaneMask|GCFont, 
  441.                      gcv);
  442.   else
  443.     XChangeGC(gDpy, gXor, 
  444.               GCForeground|GCBackground|GCFunction|GCPlaneMask|GCFont, gcv);
  445. }
  446. /* (Re)Set clipping into all GC's.
  447. */
  448. void NewClipping (x, y, w, h)
  449. int x, y, w, h;
  450. {
  451.   static XRectangle clip;    /* probably doesn't need to be static */
  452.  
  453.   /* Some servers seem to croak on negatives..
  454.   */
  455.   clip.x = (x < 0 ? 0 : x);
  456.   clip.y = (y < 0 ? 0 : y);
  457.   clip.width = (w <= 0 ? WinWidth() : w);
  458.   clip.height = (h <= 0 ? WinHeight() : h);
  459.  
  460.   XSetClipRectangles(gDpy, gText, 0, 0, &clip, 1, Unsorted);
  461.   XSetClipRectangles(gDpy, gTouchedText, 0, 0, &clip, 1, Unsorted);
  462.   XSetClipRectangles(gDpy, gXor, 0, 0, &clip, 1, Unsorted);
  463.   XSetClipRectangles(gDpy, gMode, 0, 0, &clip, 1, Unsorted);
  464. }
  465.  
  466. /* Open the display, parse arguments as needed
  467. * and prepare them for core code.
  468. */
  469. static void InitX(argc, argv, newargc, newargv)
  470. int argc, *newargc;
  471. char *argv[], *newargv[];
  472. {
  473.    unsigned long x_mask;
  474.    int    height,width,border;
  475.    int    offset;
  476.    XWMHints hints;
  477.    int i, j;
  478.  
  479.    display = (char *)getenv("DISPLAY");
  480.  
  481.   /* Cheapo argument parsing... look for font override and/or
  482.   * display override. All else packages for core editor to eat.
  483.   */
  484.     for (j = i = 0; i < argc; i++)
  485.       { 
  486.         if ((strcmp(argv[i], "-font") == 0) && (i + 1 < argc)) 
  487.           {
  488.             i++;
  489.             fontname = argv[i];
  490.           }
  491.        else if ((strcmp(argv[i], "-display") == 0) && (i + 1 < argc)) 
  492.           {
  493.             i++;
  494.             display = argv[i];
  495.           }
  496.        else if ((strcmp(argv[i], "-fg") == 0) && (i + 1 < argc)) 
  497.           {
  498.             i++;
  499.             tcolor = argv[i];
  500.           }
  501.        else if ((strcmp(argv[i], "-fg2") == 0) && (i + 1 < argc)) 
  502.           {
  503.             i++;
  504.             ccolor = argv[i];
  505.           }
  506.        else if ((strcmp(argv[i], "-bg") == 0) && (i + 1 < argc)) 
  507.           {
  508.             i++;
  509.             wcolor = argv[i];
  510.           }
  511.        else
  512.           newargv[j++] = argv[i];
  513.       }
  514.  
  515.     *newargc = j;   /* number of args */
  516.  
  517.     if ((gDpy = XOpenDisplay(display)) == NULL)
  518.       {
  519.         printf("Can not open display [%s]", display);
  520.         use();
  521.         exit(-1);
  522.       }
  523.  
  524.    /* The one and only window. 
  525.    */
  526.    x_mask = EnterWindowMask | LeaveWindowMask | ButtonPressMask | ExposureMask | 
  527.      ButtonReleaseMask | KeyPressMask | KeyReleaseMask | FocusChangeMask | 
  528.      Button1MotionMask | StructureNotifyMask | SubstructureNotifyMask;
  529.  
  530.    /* Create the window - setting white/black 
  531.    * (this is a dummy temp size which will be reset after the
  532.    *  font is loaded).
  533.    */
  534.    width = 10;
  535.    height = 10;
  536.    border = 1;
  537.    s_screen = DefaultScreen(gDpy);
  538.    s_cmap = DefaultColormap(gDpy, s_screen);
  539.    gWindow = XCreateSimpleWindow(gDpy, RootWindow(gDpy, s_screen), 
  540.                  10, 50, width, height, border, 
  541.                  BlackPixel(gDpy, s_screen), 
  542.                  WhitePixel(gDpy, s_screen));
  543.  
  544.    /* Let the windows get some events
  545.    */
  546.    XSelectInput(gDpy, gWindow, x_mask);
  547.  
  548.    /* Window manager stuff. More complete window manager
  549.    * hints would be nice.
  550.    */
  551.    hints.flags = InputHint;
  552.    hints.input = 1;
  553.    XSetWMHints(gDpy, gWindow, &hints);
  554. }
  555.  
  556. /* Set window title as needed.
  557.  */
  558. static void SetTheTitle()
  559. {
  560.   char buffer[200], dir[100];
  561. #ifndef HOSTNAMELEN   /* doesn't seem to be in a standard .h file! */
  562. # define HOSTNAMELEN 256
  563. #endif
  564.   static char hostname[HOSTNAMELEN+1] = {0};
  565.   static char lastTitle[512] = {0};
  566.   
  567.   if (!hostname[0])
  568.     gethostname(hostname, HOSTNAMELEN);
  569.   strcpy(buffer, g_APPNAME);
  570.   strcat(buffer, " ");
  571.   strcat(buffer, shortversion());
  572.   if (wdir)
  573.     strcpy(dir, wdir);
  574.   adjustnamecase(dir);
  575.   if (hostname[0])
  576.     {
  577.       strcat(buffer, "@");
  578.       strcat(buffer, hostname);
  579.     }
  580.   strcat(buffer, "   (");
  581.   strcat(buffer, dir);
  582.   strcat(buffer, ") ");
  583.   strcat(buffer, s_timestr);
  584.   if (strcmp(buffer, lastTitle) != 0)
  585.     {
  586.       XStoreName(gDpy, gWindow, buffer);
  587.       strcpy(lastTitle, buffer);        /* save last time */
  588.       WindowSync();
  589.     }
  590. }
  591.  
  592. /* Update the time string, used for window title
  593.  */
  594. static void GetTheTime()
  595. {
  596.   long currtime;
  597.   struct tm *timestruct;
  598.   
  599.   time(&currtime);
  600.   timestruct = localtime(&currtime);
  601.   sprintf(s_timestr, "  %02d:%02d\0", timestruct->tm_hour, timestruct->tm_min);
  602. }
  603.  
  604. /* Event during screen update? (ie redraw/refresh)
  605.  */
  606. BOOL WindowLookaheadEvent()
  607. {  
  608.   return(FALSE);    /* no event considered as interrupting */
  609. }
  610.  
  611. /* Dump a string to a message box; if fatal,
  612.  * leave ungracefully...
  613.  */
  614. void WindowMessage(s, fatal)
  615. char *s;
  616. BOOL fatal;
  617. {
  618.   printf("%s\n", s);        /* well, the terminal is ok for X */
  619.   if (fatal)
  620.     {
  621.       s_fatal = TRUE;
  622.       ealtmsg = TRUE;
  623.       IncrementalSave();
  624.       exit(-1);
  625.     }
  626. }
  627.  
  628. /* Simple-minded stub to exec something 
  629.  */
  630. winspawn(s, windows)
  631. char *s;
  632. BOOL windows;    /* misnamed - means peer job not monitored to completion */
  633. {
  634.   char buffer[NFILEN + L_tmpnam + 1];
  635.   pid_t parent = getpid();
  636.   pid_t oldchild = child;
  637.  
  638. #ifndef NJBOS
  639.   if (!windows)
  640.     if (child != (pid_t)-1)
  641.       {
  642.         ewprintf("Can't run mulitple shell commands at this time!");
  643.         return(FALSE);
  644.       }
  645. #endif
  646.     
  647.   if (s && *s)
  648.     {
  649. #ifndef JOBS
  650.       /* Make a log file for this parent process to
  651.       * use for all shell jobs
  652.       */
  653.       if (spawnfilename[0] == '\0')
  654. #endif
  655.         {
  656.           strcpy(spawnfilename, mktempTemplate);
  657.           mktemp(spawnfilename);
  658.         }
  659.  
  660.       if (!windows)
  661.         {
  662.           sprintf(buffer, "echo '%s shell..' > %s", g_APPNAME, spawnfilename);
  663.           system(buffer);
  664.         }
  665.  
  666.       strcpy(buffer, s);
  667.       if (!windows)
  668.         {
  669.           strcat(buffer, " 2>> ");            /* concatenate... */
  670.           strcat(buffer, spawnfilename);
  671.         }
  672.       else
  673.         strcat(buffer, "&");
  674.       ewprintf("Running 'sh %s'", buffer);
  675.  
  676.       /* Fork a child to run the system call.
  677.       * Child just exits when done, parent 
  678.       * watches for the signal.
  679.       */
  680.       if (!windows)
  681.         signal(SIGUSR1, childdone);    /* wait for child-is-done signal */
  682.       if ((child = fork()) != (pid_t)-1)
  683.         {
  684.           if (child == 0)
  685.             {
  686.               if (system(buffer) == 127)
  687.                 printf("'PID %d system call error!\n", getpid());
  688.  
  689.               if (!windows)
  690.                 {
  691. #ifndef SOL                               /* This is weird.... */
  692.                   kill(parent, SIGUSR1);
  693. #else
  694.                   sigsend(P_PID, parent, SIGUSR1);    /* tell parent */
  695. #endif
  696.                 }
  697.               _exit(0);  
  698.             }
  699.         }
  700. #ifndef NJOBS
  701.       if (windows)
  702.         child = oldchild;   /* restore state of any current job */
  703. #endif
  704.     }
  705.  
  706.   return(TRUE);
  707. }
  708. /* Captures signal to indicate
  709. * child shell process is complete
  710. */
  711. static void *childdone()
  712. {
  713.   logready = TRUE;
  714. }
  715. static void getjoblog()
  716. {
  717.   int r;
  718.  
  719.   logready = FALSE;
  720.   r = eyesno("Shell command completed, read log");
  721.   if (r == TRUE)
  722.     {
  723.       BUFFER *bp;
  724. #ifndef NJOBS
  725.       bp = bfind(spawnfilename, FALSE);
  726.  
  727.       /* Nuke any old logs from different dirs
  728.        */
  729.       if (bp)
  730.         {
  731.           bp->b_flag &= ~BFCHG;
  732.           if (strcmp(bp->b_fname, spawnfilename) != 0)
  733.             nukebuffer(bp);
  734.         }
  735. #endif                    
  736.  
  737. #if 1
  738.       ExtendedFunction(function_name(poptofilequiet));
  739.       AddString(spawnfilename);
  740.       AddKchar(CCHR('J'));
  741. #else   
  742.       AddString(spawnfilename);
  743.       AddKchar(CCHR('J'));
  744.       poptofilequiet(0, 1);   /* go get it */
  745.       ExtendedFunction(function_name(donothing));
  746.  
  747.       /* Mark the file to be deleted when buffer is
  748.        * nuked
  749.        */
  750.       if (bp = bfind(spawnfilename, FALSE))
  751.         bp->b_flag |= BFDELETE;
  752. #endif
  753.     }
  754.  
  755.   child = (pid_t)-1;    /* child is done */
  756. }
  757.  
  758. /* Find row col from pos
  759.  */
  760. void GetRowCol(prow, pcol, x, y)
  761. int *prow;
  762. int *pcol;
  763. int x;
  764. int y;
  765. {
  766.   register int row, col;
  767.   
  768.   col = (x + 1)/g_nCharWidth;
  769.   row = (y + 1)/g_nLineHeight;
  770.   if (col < 0)
  771.     col = 0;
  772.   else if (col > ncol)
  773.     col = ncol;
  774.   if (row < 0)
  775.     row = 0;
  776.   else if (row > nrow)
  777.     row = nrow;
  778.   
  779.   *pcol = col;
  780.   *prow = row;
  781. }
  782.  
  783. /* Draw cursor; s_cur_* set on draw, used on un-draw.
  784.  * Needed because ttmove moves g_caret_* w/o turning
  785.  * off caret.
  786.  */
  787. static int s_cur_x = -1, s_cur_y = -1;;
  788. static char s_cur_char;
  789. void DoShowCaret()
  790. {
  791.   MakeCaretVis(TRUE);
  792.   s_cur_x = g_caret_x;
  793.   s_cur_y = g_caret_y;
  794. #if 1
  795.   if (curwp->w_doto >= llength(curwp->w_dotp))
  796.     s_cur_char = ' ';
  797.   else
  798.     {
  799.       s_cur_char = lgetc(curwp->w_dotp, curwp->w_doto);
  800.       if ((s_cur_char < XK_space) || (s_cur_char > XK_asciitilde))
  801.         {
  802.           if (s_cur_char == '\t')
  803.             s_cur_char = ' ';
  804.           else if (ISCTRL(s_cur_char) && !eprompting)
  805.             s_cur_char = '^';
  806.           else 
  807.             s_cur_char = ' ';
  808.         }
  809.       else if (eprompting && !isearching)
  810.         s_cur_char = ' ';
  811.     }
  812. #endif
  813.   ReverseCaret(gMode);
  814. }
  815. /* Erase cursor
  816.  */
  817. void DoHideCaret()
  818. {
  819.   GC gctemp = (showtouchedlines && curwp->w_dotp->l_flag & LFCHANGE) ?
  820.               gTouchedText : gText;
  821.   MakeCaretVis(FALSE);
  822.   ReverseCaret(gctemp);
  823. }
  824. /* Do draw for cursor shape 
  825. */
  826. static void ReverseCaret(gc)
  827. GC gc;
  828. {
  829.   if ((s_cur_x == -1) || (s_cur_y == -1))
  830.     return;
  831. #if 1
  832.   XDrawImageString(gDpy, gWindow, gc, s_cur_x, yCharPos(s_cur_y), 
  833.                    &s_cur_char, 1);
  834. #else
  835.   XFillRectangle(gDpy, gWindow, gXor, s_cur_x, s_cur_y, 
  836.                  (unsigned int)g_nCharWidth, 
  837.                  (unsigned int)(g_nLineHeight - FudgeHeight));
  838. #endif
  839.   WindowFlush();
  840. }
  841. /* Manage pointer-cursor switching for some operations
  842.  */
  843. void WindowDragCursor()
  844. {
  845.   XDefineCursor(gDpy, gWindow, s_hand);
  846.   WindowFlush();
  847. }
  848. void WindowArrowCursor()
  849. {
  850.   XDefineCursor(gDpy, gWindow, s_arrow);
  851.   WindowFlush();
  852. }
  853. void WindowSizeCursor()
  854. {
  855.   XDefineCursor(gDpy, gWindow, s_size);
  856.   WindowFlush();
  857. }
  858. void WindowSleepCursor()
  859. {
  860.   if (s_fatal)
  861.     return;
  862.   XDefineCursor(gDpy, gWindow, s_wait);
  863.   WindowFlush();
  864. }
  865. void WindowNormalCursor()
  866. {
  867.   if (s_fatal)
  868.     return;
  869.   XDefineCursor(gDpy, gWindow, s_pointer);
  870.   WindowFlush();
  871. }
  872.  
  873. /* Main event loop/processing; called from core editor to
  874. * get 'keyboard' event. Returns only when a queued event
  875. * results from some X event. (Note the use of 'noop()' to
  876. * effect a return).
  877. */
  878. #define OneClick  250        /* get from resource file ! */
  879. #define IsLeft (b->button == LEFT)
  880. #define IsRight (b->button == RIGHT)
  881. #define IsDoubleClick (!IsLeftShift && (b->time - lastdown < OneClick))
  882. #define IsLeftShift (IsLeft && GetShiftState(&s_event))
  883.  
  884. void WindowGetEvent(ptr)
  885. KCHAR *ptr;
  886. {
  887.   static BOOL hackflag = FALSE;
  888.   BOOL result;
  889.   static Time lastdown = 0;
  890.   char buff[40];
  891.   int downcol, downrow; 
  892.   XButtonEvent *b = (XButtonEvent *)&s_event;
  893.   XMotionEvent *m = (XMotionEvent *)&s_event;
  894.   XExposeEvent *e = (XExposeEvent *)&s_event;
  895.   BOOL wasvis = IsCaretVis();
  896.   
  897.   /* Cursor on during wait.
  898.    */ 
  899.   if (IsCaretCreated() && (s_btndown == 0))
  900.     SetCaretVis((wasvis = TRUE));
  901.   
  902.   /* See if something there, else wait.
  903.    */
  904.   for (result = FALSE; !result; )
  905.     if (!(result = WindowReturnKCHAR(ptr)))
  906.       {
  907.         int selreturn = 0;
  908.         struct timeval tv;
  909.  
  910.         tv.tv_sec = 0;
  911.         tv.tv_usec = TIME_INC;
  912.  
  913.         /* Use select call to block waiting for new events
  914.         * IFF no events already queued. XNextEvent will flush
  915.         * output, but because select is used to simulate a timer as 
  916.         * well as process-block for i/o, an explicit flush is needed. 
  917.         * So, select will block until the connection has i/o or the 
  918.         * timeout value (set above) expires.
  919.         */
  920.         WindowFlush();
  921.     if (XEventsQueued(gDpy, QueuedAlready))
  922.       selreturn = 1;
  923.     else
  924.       {
  925.         fd_set fds;
  926.  
  927.             /* hack for no refresh when needed
  928.             */
  929.             if (hackflag)
  930.               {
  931.                 NoClipping();
  932.         ExtendedFunction(function_name(myrefresh));
  933.                 hackflag = FALSE;
  934.                 goto out;        /* ughly */
  935.               }
  936.  
  937.         FD_ZERO(&fds);
  938.         FD_SET(ConnectionNumber(gDpy), &fds);
  939. #if defined(SOL) || defined(HP)
  940.         if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
  941.           {
  942.             selreturn = select(rlim.rlim_cur, &fds, 0, 0, &tv);
  943.           }
  944.         else
  945.           {
  946.             ewprintf("getrlimit failed!");
  947.         ttbeep();
  948.                 continue;
  949.               }
  950. #else
  951.         selreturn = select(getdtablesize(), &fds, 0, 0, &tv);
  952. #endif
  953.       }
  954.  
  955.         /* 'selreturn' indicates state; events waiting (ie previous
  956.         * events or new), the timer 'ticked' or an error occurred.
  957.         */
  958.         if (selreturn < 0)
  959.           {
  960.             ttbeep();
  961.             continue;    /* what else to do? try again */
  962.           }
  963.  
  964.         /* NULL means timer expired w/o traffic on socket
  965.         */
  966.         else if (selreturn == 0)
  967.           DoTimer();
  968.     
  969.     /* Process and return if result of this event 
  970.         * yields a KCHAR. To get here means XNextEvent 
  971.         * will not block because something is in the queue.
  972.     */
  973.     else 
  974.           {
  975.             XNextEvent(gDpy, &s_event);
  976.         switch(s_event.type)
  977.             {
  978.         case FocusIn:
  979.         SetCaretCreated(TRUE);
  980.         SetCaretVis(TRUE);
  981.         g_hasFocus = TRUE;
  982.                 break;
  983.  
  984.         case FocusOut: 
  985. #if 0
  986.         SetCaretVis(FALSE);
  987.         SetCaretCreated(FALSE);
  988. #endif
  989.         g_hasFocus = FALSE;
  990.         break;
  991.  
  992.             case ConfigureNotify:
  993.                 {
  994.                   int save_ncol = ncol;
  995.                   int save_nrow = nrow;
  996.  
  997.                   WindowSync();
  998.                   WindowGetSize(&nrow, &ncol);
  999.                   if ((nrow == save_nrow) && (ncol == save_ncol))
  1000.                     break;
  1001.  
  1002.                   /* BUG PRONE - this is basically doing
  1003.                   * a hack to get a resize but not draw
  1004.                   * a zillion times; most every resize gets
  1005.                   * a corresponding refresh. Note the
  1006.                   * hack flag...
  1007.                   */
  1008.                   NewClipping(0, 0, 1, 1);
  1009.                   hackflag = TRUE;
  1010.           ExtendedFunction(function_name(myrefresh));
  1011.           break;
  1012.                 }
  1013.  
  1014.         case Expose:
  1015.         case GraphicsExpose:
  1016.               {
  1017.                 int row = ttrow;    /* save cursor */
  1018.                 int col = ttcol;
  1019.  
  1020.                 /* clip to damaged area, repair window..
  1021.                  * and restore clipping
  1022.                  */
  1023.                 NewClipping(e->x, e->y, e->width, e->height);
  1024.                 if (wasvis)
  1025.               SetCaretVis(FALSE);
  1026.                 sgarbf = TRUE;
  1027.                 update();
  1028.                 if (wasvis)
  1029.               SetCaretVis(TRUE);
  1030.                 NoClipping();
  1031.                 hackflag = FALSE;
  1032.                 if (isearching)
  1033.                   noop(); /* jumpSearch(); hacky research to refresh */
  1034.                 else if (eprompting)
  1035.                   erepair();         /* fix prompt+msg */
  1036.                 else 
  1037.                   ExtendedFunction(function_name(donothing));
  1038.                 ttmove(row, col);     /* restore cursor */
  1039.         break;
  1040.               }
  1041.  
  1042.         case MotionNotify:
  1043.         GetRowCol(&downrow, &downcol, m->x, m->y);
  1044.         if ((s_btndown & LEFT) && !(s_btndown & RIGHT))
  1045.           {
  1046.             ExtendedFunction(function_name(mousecmd));
  1047.             AddString(MoveStr);
  1048.             sprintf(buff, posFormat, downrow, downcol);
  1049.             AddString(buff);
  1050.           }
  1051.                 break;
  1052.  
  1053.         case ButtonRelease:
  1054.         GetRowCol(&downrow, &downcol, b->x, b->y);
  1055.         if (IsLeft && (s_btndown & LEFT))
  1056.           {
  1057.                 s_btndown &= ~LEFT;
  1058.             ExtendedFunction(function_name(mousecmd));
  1059.             AddString(LeftUp);
  1060.             sprintf(buff, posFormat, downrow, downcol);
  1061.             AddString(buff);
  1062.               }
  1063.         else if (IsRight && (s_btndown & RIGHT))
  1064.                   {
  1065.             s_btndown &= ~RIGHT;
  1066.             noop();
  1067.           }
  1068.                 break;
  1069.  
  1070.         case ButtonPress:
  1071.         GetRowCol(&downrow, &downcol, b->x, b->y);
  1072.  
  1073.                 switch(b->button)
  1074.                   {
  1075.                 case RIGHT:
  1076.               s_btndown |= RIGHT;
  1077.           ExtendedFunction(function_name(mousecmd));
  1078.           if (s_btndown & LEFT)
  1079.                     {
  1080.                       AddString(MouseAbort);
  1081.                       AddKchar(' ');
  1082.                     }
  1083.           else
  1084.                 {
  1085.                   AddString(RightDwn);
  1086.               sprintf(buff, posFormat, downrow, downcol);
  1087.               AddString(buff);
  1088.                 }
  1089.                   break;
  1090.  
  1091.                 case LEFT:
  1092.           s_btndown |= LEFT;
  1093.           ExtendedFunction(function_name(mousecmd));
  1094.           if (s_btndown & RIGHT)
  1095.                     {
  1096.                       AddString(MouseAbort);
  1097.                       AddKchar(' ');
  1098.                     }
  1099.           else
  1100.             {
  1101.                   /* doubleclick left is special
  1102.               */
  1103.               if (IsDoubleClick)
  1104.             {
  1105.               AddString(DoubleClick);
  1106.               AddKchar(' ');
  1107.                           lastdown = b->time;
  1108.                           break;            /* no other params! */
  1109.             } 
  1110.               else if (IsLeftShift)
  1111.             AddString(LeftDwnShift);
  1112.                       else                /* normal left */
  1113.                     AddString(LeftDwn);
  1114.                   sprintf(buff, posFormat, downrow, downcol);
  1115.               AddString(buff);
  1116.                       lastdown = b->time;
  1117.                 }
  1118.                   break;
  1119.  
  1120.                 default:
  1121.                   ewprintf("Unsupported button");
  1122.                   ttbeep();
  1123.                   break;
  1124.                   }
  1125.                 break;
  1126.  
  1127.         case KeyRelease:    
  1128.         case KeyPress:    
  1129.         /* Keyboard input events cause some delay to autosave
  1130.          */
  1131.                 if (s_event.type == KeyPress)
  1132.                   {
  1133.             s_sTicks -= SAVE_DELAY;
  1134.             if (s_sTicks < 0)
  1135.               s_sTicks = 0;
  1136.                   }
  1137.  
  1138.                 /* prevent garbled commands interrupting mouse
  1139.                 */
  1140.                 if (!s_btndown)
  1141.           WindowMapKey((XKeyEvent *)&s_event, s_event.type == KeyPress);
  1142.         break;
  1143.  
  1144.           default:
  1145.         break;
  1146.             }        /* switch */
  1147.           }        /* XNextEvent */
  1148.       }        /* for () */
  1149.  
  1150.   
  1151.   /* cursor off during process
  1152.    */
  1153. out:                    /* I hate goto's.... */
  1154.   if (wasvis)
  1155.     SetCaretVis(FALSE);
  1156. }
  1157.  
  1158. /* Process a 'timer' message; really result of timeout
  1159. * on select call as opposed to actual event.
  1160. */ 
  1161. static void DoTimer()
  1162. {
  1163.   static int tTicks = 0;          /* Title update interval */
  1164.   static BOOL incallback = FALSE; /* Prevent recursion in WM_TIMER 
  1165.                          triggered routine calls */
  1166.  
  1167.   if (edInited)
  1168.     {
  1169.       /* Log ready? Try to be polite about interrupting the
  1170.       * user. (Ughly code here!)
  1171.       */
  1172.       if (!isearching && !eprompting && !s_btndown)
  1173.         {
  1174.           /* What time is it?
  1175.           */
  1176.           if (!incallback)
  1177.             {
  1178.               incallback = TRUE;
  1179.               AnyPendingAlarms();
  1180.               incallback = FALSE;
  1181.             }
  1182.           /* spawned job ran
  1183.           */
  1184.           if (logready)
  1185.             {
  1186.               SetCaretVis(FALSE);
  1187.               getjoblog();
  1188.               noop();
  1189.             }
  1190.         }
  1191.  
  1192.       /* counter for incremental save (keyboard input delays this)
  1193.        */
  1194.       s_sTicks++;
  1195.       if ((s_sTicks > INCS_PER_SAVE) && !incallback && !eprompting)
  1196.     {
  1197.       /* Any messages printed by routines called
  1198.        * from a timer must not use standard 
  1199.        * output method (ie async)
  1200.        */
  1201.       ealtmsg = TRUE;
  1202.       incallback = TRUE;
  1203.       IncrementalSave();
  1204.       incallback = FALSE;
  1205.       s_sTicks = 0;
  1206.       ealtmsg = FALSE;
  1207.         }
  1208.  
  1209.       /* Counter for titlebar update
  1210.       */
  1211.       tTicks++;
  1212.       if (tTicks > INCS_PER_UPDATE)
  1213.     {
  1214.       GetTheTime();     /* get current clock time */
  1215.       SetTheTitle();    /* fix window title */ 
  1216.       tTicks = 0;
  1217.     }
  1218.       
  1219.       /* auto scroll mode line
  1220.        */
  1221.        if ((s_btndown & LEFT) && GetCtrlState(&s_event))
  1222.          {
  1223.            s_scrollTicks++;
  1224.            if (s_scrollTicks >= DELAY_TO_SCROLL)
  1225.              {
  1226.                char buff[8];
  1227.  
  1228.                  ExtendedFunction(function_name(mousecmd)); 
  1229.                AddString(MouseTimer);
  1230.                sprintf(buff, " %d ", GetShiftState(&s_event));
  1231.                AddString(buff);
  1232.                s_scrollTicks = DELAY_TO_SCROLL;    /* prevent overflow */
  1233.              }
  1234.          }
  1235.        else
  1236.          s_scrollTicks = 0;    /* force reset */
  1237.  
  1238.        /* Mail status changed?
  1239.        */
  1240.        anynewmail();
  1241.     }
  1242. }
  1243. void WindowSync()
  1244. {
  1245.   XSync(gDpy, FALSE);
  1246. }
  1247. void WindowFlush()
  1248. {
  1249.   ttcharflush();
  1250.   XFlush(gDpy);
  1251. }
  1252.  
  1253. static void use()
  1254. {
  1255.   printf("\nusage: %s [-font font] [-display dpy] [-fg] [-fg2] [-bg] [files]\n", 
  1256.          whoami);
  1257. }
  1258.  
  1259. /* sleep hack - not so long..
  1260. */
  1261. unsigned sleep(x)
  1262. unsigned int x;
  1263. {
  1264.   /* x is number of seconds; this is a major hack to 
  1265.   * wait for a timeout; note NO real fd's used. This works
  1266.   * because no i/o can normally occur to cause a wakeup,
  1267.   * and the timeout value can be tuned so sleeping isn't such a 
  1268.   * lengthy process (ie x=1 is about 3/4 of a sec).
  1269.   */
  1270.   int selreturn = 0;
  1271.   struct timeval tv;
  1272.   fd_set fds;
  1273.  
  1274.   tv.tv_sec = 0;
  1275.   tv.tv_usec = x * TIME_INC;
  1276.   
  1277.   FD_ZERO(&fds);
  1278. #if defined(SOL) || defined(HP)
  1279.  
  1280.   if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
  1281.     selreturn = select(rlim.rlim_cur, &fds, 0, 0, &tv);
  1282.   else
  1283.     {
  1284.       ewprintf("getrlimit failed!");
  1285.       ttbeep();
  1286.     }
  1287. #else
  1288.   selreturn = select(getdtablesize(), &fds, 0, 0, &tv);
  1289. #endif
  1290. }
  1291. #endif /** JAM **/
  1292.